package planner.plan;

import java.util.ArrayList;
import java.util.Iterator;

import planner.domain.Operator;

public class Plan {

	private ArrayList<PlanStep> opSequence;
	private ArrayList<String> initialConditions;
	
	private boolean debug = false;

	public Plan() {
		initialConditions = new ArrayList<String>();
		opSequence = new ArrayList<PlanStep>();
	}
	
	public Plan(ArrayList<String> ic) {
		initialConditions = (ArrayList<String>) ic.clone();
		opSequence = new ArrayList<PlanStep>();
	}

	// reversed

	public void addPartialPlan(ArrayList<Operator> operators) {
		for (int i = 0; i < operators.size(); i++) {
			// Operator op = operators.get(operators.size() - 1 - i);
			Operator op = operators.get(i);
			PlanStep a = new PlanStep(op);
			addPlannedAction(a);
		}
	}
	
	private boolean canShiftStep(String opName){
		Iterator<PlanStep> iSteps = opSequence.iterator();
		while (iSteps.hasNext()) {
			PlanStep planStep = (PlanStep) iSteps.next();
			if(planStep.getOperatorName().equals(opName)){
				planStep.clearSolvedConditions();
				return true;
			}
		}
		return false;
	}
	
	private void shiftPlanStep(PlanStep p){
		PlanStep shiftStep = null;
		Iterator<PlanStep> iSteps = opSequence.iterator();
		
		while (iSteps.hasNext()) {
			PlanStep planStep = (PlanStep) iSteps.next();
			if(planStep.getOperatorName().equals(p.getOperatorName())){
				shiftStep = planStep;
				break;
			}				
		}
		if(shiftStep != null && debug){
			System.out.println("Shifting Step: " + shiftStep.getOperatorName());
			System.out.println("Step has " + shiftStep.getPreviousSteps().size() + " previous Steps and " + shiftStep.getChildSteps().size() + " child Steps");
		}
		
		shiftStep.removeChildSteps();
		
		ArrayList<PlanStep> previousSteps = shiftStep.getPreviousSteps();
		ArrayList<PlanStep> childSteps = shiftStep.getChildSteps();
		
		Iterator<PlanStep> iPreviousSteps = previousSteps.iterator();		
		
		while(iPreviousSteps.hasNext()){
			PlanStep previous = iPreviousSteps.next();
			Iterator<PlanStep> iChildSteps = childSteps.iterator();
			
			while(iChildSteps.hasNext()){
				PlanStep child = iChildSteps.next();
				child.addPreviousStep(previous);
			}			
		}
		
		opSequence.remove(shiftStep);
		
	}
	
	public boolean containsOperator(Operator o){
		Iterator<PlanStep> iPS = opSequence.iterator();
		while (iPS.hasNext()) {
			PlanStep planStep = (PlanStep) iPS.next();
			if(planStep.getOperatorName().equals(o.getName()))
				return true;
		}
		
		return false;
	}
	
	public void addPlannedAction(PlanStep a) {
		if(canShiftStep(a.getOperatorName()))
			shiftPlanStep(a);
		
		if(opSequence.isEmpty())
			a.setLevel(0);
			
		Iterator<PlanStep> iPS = opSequence.iterator();
			
		PlanStep higherStepWithCondition = null;
		PlanStep higherStep = null;
			
		while (iPS.hasNext()) {
			PlanStep planStep = (PlanStep) iPS.next();
				
			if(higherStep == null)
				higherStep = planStep;
				
			else if (planStep.getLevel() > higherStep.getLevel())
				higherStep = planStep;			
				
			if (planStep.hasOpenPC() && planStep.hasConditionSolvedBy(a)) {
				
				planStep.removeFromOpenConditions(a.getOperatorEffects());
				
				if(higherStepWithCondition == null)
					higherStepWithCondition = planStep;
					
				else if(higherStepWithCondition.getLevel() < planStep.getLevel())
						higherStepWithCondition = planStep;				
			}			
		}
			
		if(higherStepWithCondition != null){
			if(debug)
				System.out.println("Higher Step with Condition " + higherStepWithCondition.getOperatorName());
			higherStepWithCondition.addPreviousStep(a);
		}
			
		else if (higherStep != null){
			if(debug)
				System.out.println("Higher Step " + higherStep.getOperatorName());
			higherStep.addPreviousStep(a);
		}
		
		opSequence.add(a);		
	}
	
	public ArrayList<PlanStep> getActionSequence() {
		return opSequence;
	}
	
	private void addActionSequence(ArrayList<PlanStep> actionSeq){
		opSequence.addAll(actionSeq);
	}
	
	public void printPlan() {
		Iterator<PlanStep> it = opSequence.iterator();

		while (it.hasNext()) {
			PlanStep planStep = (PlanStep) it.next();
			System.out.println(planStep.getLevel()+ " : " + planStep.getOperatorName() );
			System.out.print("Previous Steps: ");
			planStep.printPreviousStep();
			System.out.println();
		}
		System.out.println();
	}

	public void remove(ArrayList<PlanStep> removedActions) {
		// TODO Auto-generated method stub
		opSequence.removeAll(removedActions);
	}

	public PlanStep getStepWithCondition(String condition) {
		Iterator<PlanStep> iPS = opSequence.iterator();
		while (iPS.hasNext()) {
			PlanStep planStep = (PlanStep) iPS.next();
			if (planStep.hasCondition(condition))
				return planStep;
		}

		return null;
	}

	public ArrayList<String> getOpenPreConditions() {
		ArrayList<String> openPC = new ArrayList<String>();
		Iterator<PlanStep> iPS = opSequence.iterator();

		while (iPS.hasNext()) {
			PlanStep planStep = (PlanStep) iPS.next();
			if (planStep.hasOpenPC()) {
				ArrayList<String> openPreConditions = planStep.getOpenPreConditions(); 
				Iterator<String> iPC = openPreConditions.iterator();
				while(iPC.hasNext()){
					String nextPC = iPC.next();
					if(!openPC.contains(nextPC))
						openPC.add(nextPC);
				}
			}
		}
		
		openPC.removeAll(initialConditions);

		return openPC;
	}

	// TODO trocar para pegar o operador "goal"
	public PlanStep getLastStep() {
		return opSequence.get(0);
	}

	public void mergeNextPlan(Plan nextPlan) {
		if(opSequence.isEmpty()){
			opSequence = nextPlan.getActionSequence();
		}
		else{
			if(debug)
				getLastStep().printPreviousStep();
			
			ArrayList<PlanStep> ends = getLastStep().getPreviousSteps();

			opSequence.remove(getLastStep());
			opSequence.removeAll(ends);
						
			Iterator<PlanStep> iEnd = ends.iterator();
			
			while (iEnd.hasNext()) {
				PlanStep planStep = (PlanStep) iEnd.next();
				nextPlan.addPlannedAction(planStep);
			}
			
			nextPlan.addActionSequence(getActionSequence());
			
			opSequence = nextPlan.getActionSequence();
			updateLvls();
		}
	}

	private void updateLvls(){
		ArrayList<PlanStep> left2Update = new ArrayList<PlanStep>();		
		left2Update.add(getLastStep());
		while(!left2Update.isEmpty()){
			PlanStep currentLvl = left2Update.remove(0);
			Iterator<PlanStep> previousSteps = currentLvl.getPreviousSteps().iterator();
			
			while (previousSteps.hasNext()) {
				PlanStep planStep = (PlanStep) previousSteps.next();
				planStep.setLevel(currentLvl.getLevel() + 1);
			}
			
			left2Update.addAll(currentLvl.getPreviousSteps());
		}
	}
	
	public int getSize() {
		return opSequence.size();
	}
	
	public void setOpSequence(ArrayList<PlanStep> sequence){
		if(sequenceContainsGoal(sequence))
			opSequence.addAll(sequence);
		else{
			PlanStep goal = new PlanStep("goal", new ArrayList<String>());
			addPlannedAction(goal);
			int lowest = getLowestLvlFromSequence(sequence);
			Iterator<PlanStep> iSequence = sequence.iterator();
			while (iSequence.hasNext()) {
				PlanStep planStep = (PlanStep) iSequence.next();
				if(planStep.getLevel() == lowest)
					planStep.addPreviousStep(goal);
			}
			opSequence.addAll(sequence);
		}
	}
	
	private boolean sequenceContainsGoal(ArrayList<PlanStep> sequence){
		Iterator<PlanStep> iSequence = sequence.iterator();
		while (iSequence.hasNext()) {
			PlanStep planStep = (PlanStep) iSequence.next();
			if(planStep.getOperatorName() == "goal")
				return true;
		}
		return false;
	}
	
	private int getLowestLvlFromSequence(ArrayList<PlanStep> sequence){
		int lowest = sequence.get(0).getLevel();
		Iterator<PlanStep> iStep = sequence.iterator();
		while (iStep.hasNext()) {
			PlanStep planStep = (PlanStep) iStep.next();
			if(planStep.getLevel() < lowest)
				lowest = planStep.getLevel();
		}
		return lowest;
	}
}
